home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / gnu / groff_src.lha / Groff-1.07 / grops / ps.cc < prev    next >
C/C++ Source or Header  |  1993-02-21  |  36KB  |  1,506 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "driver.h"
  22. #include "stringclass.h"
  23. #include "cset.h"
  24.  
  25. #include "ps.h"
  26.  
  27. static int landscape_flag = 0;
  28. static int ncopies = 1;
  29. static int linewidth = -1;
  30. // Non-zero means generate PostScript code that guesses the paper
  31. // length using the imageable area.
  32. static int guess_flag = 0;
  33.  
  34. // Non-zero if -b was specified on the command line.
  35. static int bflag = 0;
  36. unsigned broken_flags = 0;
  37.  
  38. #define DEFAULT_LINEWIDTH 40    /* in ems/1000 */
  39. #define FILL_MAX 1000
  40.  
  41. const char *const dict_name = "grops";
  42. const char *const defs_dict_name = "DEFS";
  43. const int DEFS_DICT_SPARE = 50;
  44.  
  45. double degrees(double r)
  46. {
  47.   return r*180.0/M_PI;
  48. }
  49.  
  50. double radians(double d)
  51. {
  52.   return d*M_PI/180.0;
  53. }
  54.  
  55. inline double transform_fill(int fill)
  56. {
  57.   return 1 - fill/double(FILL_MAX);
  58. }
  59.  
  60. ps_output::ps_output(FILE *f, int n)
  61. : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
  62. {
  63. }
  64.  
  65. ps_output &ps_output::set_file(FILE *f)
  66. {
  67.   fp = f;
  68.   col = 0;
  69.   return *this;
  70. }
  71.  
  72. ps_output &ps_output::copy_file(FILE *infp)
  73. {
  74.   int c;
  75.   while ((c = getc(infp)) != EOF)
  76.     putc(c, fp);
  77.   return *this;
  78. }
  79.  
  80. ps_output &ps_output::end_line()
  81. {
  82.   if (col != 0) {
  83.     putc('\n', fp);
  84.     col = 0;
  85.     need_space = 0;
  86.   }
  87.   return *this;
  88. }
  89.  
  90. ps_output &ps_output::special(const char *s)
  91. {
  92.   if (s == 0 || *s == '\0')
  93.     return *this;
  94.   if (col != 0) {
  95.     putc('\n', fp);
  96.     col = 0;
  97.   }
  98.   fputs(s, fp);
  99.   if (strchr(s, '\0')[-1] != '\n')
  100.     putc('\n', fp);
  101.   need_space = 0;
  102.   return *this;
  103. }
  104.  
  105. ps_output &ps_output::simple_comment(const char *s)
  106. {
  107.   if (col != 0)
  108.     putc('\n', fp);
  109.   putc('%', fp);
  110.   putc('%', fp);
  111.   fputs(s, fp);
  112.   putc('\n', fp);
  113.   col = 0;
  114.   need_space = 0;
  115.   return *this;
  116. }
  117.  
  118. ps_output &ps_output::begin_comment(const char *s)
  119. {
  120.   if (col != 0)
  121.     putc('\n', fp);
  122.   putc('%', fp);
  123.   putc('%', fp);
  124.   fputs(s, fp);
  125.   col = 2 + strlen(s);
  126.   return *this;
  127. }
  128.  
  129. ps_output &ps_output::end_comment()
  130. {
  131.   if (col != 0) {
  132.     putc('\n', fp);
  133.     col = 0;
  134.   }
  135.   need_space = 0;
  136.   return *this;
  137. }
  138.  
  139. ps_output &ps_output::comment_arg(const char *s)
  140. {
  141.   int len = strlen(s);
  142.   if (col + len + 1 > max_line_length) {
  143.     putc('\n', fp);
  144.     fputs("%%+", fp);
  145.     col = 3;
  146.   }
  147.   putc(' ',  fp);
  148.   fputs(s, fp);
  149.   col += len + 1;
  150.   return *this;
  151. }
  152.  
  153. ps_output &ps_output::set_fixed_point(int n)
  154. {
  155.   assert(n >= 0 && n <= 10);
  156.   fixed_point = n;
  157.   return *this;
  158. }
  159.  
  160. ps_output &ps_output::put_delimiter(char c)
  161. {
  162.   if (col + 1 > max_line_length) {
  163.     putc('\n', fp);
  164.     col = 0;
  165.   }
  166.   putc(c, fp);
  167.   col++;
  168.   need_space = 0;
  169.   return *this;
  170. }
  171.  
  172. ps_output &ps_output::put_string(const char *s, int n)
  173. {
  174.   int len = 0;
  175.   for (int i = 0; i < n; i++) {
  176.     char c = s[i];
  177.     if (isascii(c) && isprint(c)) {
  178.       if (c == '(' || c == ')' || c == '\\')
  179.     len += 2;
  180.       else
  181.     len += 1;
  182.     }
  183.     else
  184.       len += 4;
  185.   }
  186.   if (len > n*2) {
  187.     if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
  188.       putc('\n', fp);
  189.       col = 0;
  190.     }
  191.     if (col + 1 > max_line_length) {
  192.       putc('\n', fp);
  193.       col = 0;
  194.     }
  195.     putc('<', fp);
  196.     col++;
  197.     for (i = 0; i < n; i++) {
  198.       if (col + 2 > max_line_length) {
  199.     putc('\n', fp);
  200.     col = 0;
  201.       }
  202.       fprintf(fp, "%02x", s[i] & 0377);
  203.       col += 2;
  204.     }
  205.     putc('>', fp);
  206.     col++;
  207.   }
  208.   else {
  209.     if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
  210.       putc('\n', fp);
  211.       col = 0;
  212.     }
  213.     if (col + 2 > max_line_length) {
  214.       putc('\n', fp);
  215.       col = 0;
  216.     }
  217.     putc('(', fp);
  218.     col++;
  219.     for (i = 0; i < n; i++) {
  220.       char c = s[i];
  221.       if (isascii(c) && isprint(c)) {
  222.     if (c == '(' || c == ')' || c == '\\')
  223.       len = 2;
  224.     else
  225.       len = 1;
  226.       }
  227.       else
  228.     len = 4;
  229.       if (col + len + 1 > max_line_length) {
  230.     putc('\\', fp);
  231.     putc('\n', fp);
  232.     col = 0;
  233.       }
  234.       switch (len) {
  235.       case 1:
  236.     putc(c, fp);
  237.     break;
  238.       case 2:
  239.     putc('\\', fp);
  240.     putc(c, fp);
  241.     break;
  242.       case 4:
  243.     fprintf(fp, "\\%03o", c & 0377);
  244.     break;
  245.       default:
  246.     assert(0);
  247.       }
  248.       col += len;
  249.     }
  250.     putc(')', fp);
  251.     col++;
  252.   }
  253.   need_space = 0;
  254.   return *this;
  255. }
  256.  
  257. ps_output &ps_output::put_number(int n)
  258. {
  259.   char buf[1 + INT_DIGITS + 1];
  260.   sprintf(buf, "%d", n);
  261.   int len = strlen(buf);
  262.   if (col > 0 && col + len + need_space > max_line_length) {
  263.     putc('\n', fp);
  264.     col = 0;
  265.     need_space = 0;
  266.   }
  267.   if (need_space) {
  268.     putc(' ', fp);
  269.     col++;
  270.   }
  271.   fputs(buf, fp);
  272.   col += len;
  273.   need_space = 1;
  274.   return *this;
  275. }
  276.  
  277. ps_output &ps_output::put_fix_number(int i)
  278. {
  279.   const char *p = iftoa(i, fixed_point);
  280.   int len = strlen(p);
  281.   if (col > 0 && col + len + need_space > max_line_length) {
  282.     putc('\n', fp);
  283.     col = 0;
  284.     need_space = 0;
  285.   }
  286.   if (need_space) {
  287.     putc(' ', fp);
  288.     col++;
  289.   }
  290.   fputs(p, fp);
  291.   col += len;
  292.   need_space = 1;
  293.   return *this;
  294. }
  295.  
  296. ps_output &ps_output::put_float(double d)
  297. {
  298.   char buf[128];
  299.   sprintf(buf, "%.4f", d);
  300.   int len = strlen(buf);
  301.   if (col > 0 && col + len + need_space > max_line_length) {
  302.     putc('\n', fp);
  303.     col = 0;
  304.     need_space = 0;
  305.   }
  306.   if (need_space) {
  307.     putc(' ', fp);
  308.     col++;
  309.   }
  310.   fputs(buf, fp);
  311.   col += len;
  312.   need_space = 1;
  313.   return *this;
  314. }
  315.  
  316. ps_output &ps_output::put_symbol(const char *s)
  317. {
  318.   int len = strlen(s);
  319.   if (col > 0 && col + len + need_space > max_line_length) {
  320.     putc('\n', fp);
  321.     col = 0;
  322.     need_space = 0;
  323.   }
  324.   if (need_space) {
  325.     putc(' ', fp);
  326.     col++;
  327.   }
  328.   fputs(s, fp);
  329.   col += len;
  330.   need_space = 1;
  331.   return *this;
  332. }
  333.  
  334. ps_output &ps_output::put_literal_symbol(const char *s)
  335. {
  336.   int len = strlen(s);
  337.   if (col > 0 && col + len + 1 > max_line_length) {
  338.     putc('\n', fp);
  339.     col = 0;
  340.   }
  341.   putc('/', fp);
  342.   fputs(s, fp);
  343.   col += len + 1;
  344.   need_space = 1;
  345.   return *this;
  346. }
  347.  
  348. class ps_font : public font {
  349.   ps_font(const char *);
  350. public:
  351.   int encoding_index;
  352.   char *encoding;
  353.   char *reencoded_name;
  354.   ~ps_font();
  355.   void handle_unknown_font_command(const char *command, const char *arg,
  356.                    const char *filename, int lineno);
  357.   static ps_font *load_ps_font(const char *);
  358. };
  359.  
  360. ps_font *ps_font::load_ps_font(const char *s)
  361. {
  362.   ps_font *f = new ps_font(s);
  363.   if (!f->load()) {
  364.     delete f;
  365.     return 0;
  366.   }
  367.   return f;
  368. }
  369.  
  370. ps_font::ps_font(const char *nm)
  371. : font(nm), encoding(0), reencoded_name(0), encoding_index(-1)
  372. {
  373. }
  374.  
  375. ps_font::~ps_font()
  376. {
  377.   a_delete encoding;
  378.   a_delete reencoded_name;
  379. }
  380.  
  381. void ps_font::handle_unknown_font_command(const char *command, const char *arg,
  382.                       const char *filename, int lineno)
  383. {
  384.   if (strcmp(command, "encoding") == 0) {
  385.     if (arg == 0)
  386.       error_with_file_and_line(filename, lineno,
  387.                    "`encoding' command requires an argument");
  388.     else
  389.       encoding = strsave(arg);
  390.   }
  391. }
  392.  
  393. static void handle_unknown_desc_command(const char *command, const char *arg,
  394.                     const char *filename, int lineno)
  395. {
  396.   if (strcmp(command, "broken") == 0) {
  397.     if (arg == 0)
  398.       error_with_file_and_line(filename, lineno,
  399.                    "`broken' command requires an argument");
  400.     else if (!bflag)
  401.       broken_flags = atoi(arg);
  402.   }
  403. }
  404.  
  405. struct style {
  406.   font *f;
  407.   int point_size;
  408.   int height;
  409.   int slant;
  410.   style();
  411.   style(font *, int, int, int);
  412.   int operator==(const style &) const;
  413.   int operator!=(const style &) const;
  414. };
  415.  
  416. style::style() : f(0)
  417. {
  418. }
  419.  
  420. style::style(font *p, int sz, int h, int sl)
  421. : f(p), point_size(sz), height(h), slant(sl)
  422. {
  423. }
  424.  
  425. int style::operator==(const style &s) const
  426. {
  427.   return (f == s.f && point_size == s.point_size
  428.       && height == s.height && slant == s.slant);
  429. }
  430.  
  431. int style::operator!=(const style &s) const
  432. {
  433.   return !(*this == s);
  434. }
  435.  
  436. class ps_printer : public printer {
  437.   FILE *tempfp;
  438.   ps_output out;
  439.   int res;
  440.   int space_char_index;
  441.   int pages_output;
  442.   int paper_length;
  443.   int equalise_spaces;
  444.   enum { SBUF_SIZE = 256 };
  445.   char sbuf[SBUF_SIZE];
  446.   int sbuf_len;
  447.   int sbuf_start_hpos;
  448.   int sbuf_vpos;
  449.   int sbuf_end_hpos;
  450.   int sbuf_space_width;
  451.   int sbuf_space_count;
  452.   int sbuf_space_diff_count;
  453.   int sbuf_space_code;
  454.   int sbuf_kern;
  455.   style sbuf_style;
  456.   style output_style;
  457.   int output_hpos;
  458.   int output_vpos;
  459.   int output_draw_point_size;
  460.   int line_thickness;
  461.   int output_line_thickness;
  462.   int fill;
  463.   unsigned char output_space_code;
  464.   enum { MAX_DEFINED_STYLES = 50 };
  465.   style defined_styles[MAX_DEFINED_STYLES];
  466.   int ndefined_styles;
  467.   int next_encoding_index;
  468.   string defs;
  469.   int ndefs;
  470.   resource_manager rm;
  471.   int invis_count;
  472.  
  473.   void flush_sbuf();
  474.   void set_style(const style &);
  475.   void set_space_code(unsigned char c);
  476.   int set_encoding_index(ps_font *);
  477.   void do_exec(char *, const environment *);
  478.   void do_import(char *, const environment *);
  479.   void do_def(char *, const environment *);
  480.   void do_mdef(char *, const environment *);
  481.   void do_file(char *, const environment *);
  482.   void do_invis(char *, const environment *);
  483.   void do_endinvis(char *, const environment *);
  484.   void set_line_thickness(const environment *);
  485.   void fill_path();
  486.   void encode_fonts();
  487.   void define_encoding(const char *, int);
  488.   void reencode_font(ps_font *);
  489. public:
  490.   ps_printer();
  491.   ~ps_printer();
  492.   void set_char(int i, font *f, const environment *env, int w);
  493.   void draw(int code, int *p, int np, const environment *env);
  494.   void begin_page(int);
  495.   void end_page(int);
  496.   void special(char *arg, const environment *env);
  497.   font *make_font(const char *);
  498.   void end_of_line();
  499. };
  500.  
  501. ps_printer::ps_printer()
  502. : pages_output(0),
  503.   sbuf_len(0),
  504.   output_hpos(-1),
  505.   output_vpos(-1),
  506.   out(0, 79),
  507.   ndefined_styles(0),
  508.   next_encoding_index(0),
  509.   line_thickness(-1),
  510.   fill(FILL_MAX + 1),
  511.   ndefs(0),
  512.   invis_count(0)
  513. {
  514.   tempfp = xtmpfile();
  515.   out.set_file(tempfp);
  516.   if (linewidth < 0)
  517.     linewidth = DEFAULT_LINEWIDTH;
  518.   if (font::hor != 1)
  519.     fatal("horizontal resolution must be 1");
  520.   if (font::vert != 1)
  521.     fatal("vertical resolution must be 1");
  522.   if (font::res % (font::sizescale*72) != 0)
  523.     fatal("res must be a multiple of 72*sizescale");
  524.   int r = font::res;
  525.   int point = 0;
  526.   while (r % 10 == 0) {
  527.     r /= 10;
  528.     point++;
  529.   }
  530.   res = r;
  531.   out.set_fixed_point(point);
  532.   space_char_index = font::name_to_index("space");
  533.   paper_length = font::paperlength;
  534.   if (paper_length == 0)
  535.     paper_length = 11*font::res;
  536.   equalise_spaces = font::res >= 72000;
  537. }
  538.  
  539. int ps_printer::set_encoding_index(ps_font *f)
  540. {
  541.   if (f->encoding_index >= 0)
  542.     return f->encoding_index;
  543.   for (font_pointer_list *p = font_list; p; p = p->next)
  544.     if (p->p != f) {
  545.       char *encoding = ((ps_font *)p->p)->encoding;
  546.       int encoding_index = ((ps_font *)p->p)->encoding_index;
  547.       if (encoding != 0 && encoding_index >= 0 
  548.       && strcmp(f->encoding, encoding) == 0) {
  549.     return f->encoding_index = encoding_index;
  550.       }
  551.     }
  552.   return f->encoding_index = next_encoding_index++;
  553. }
  554.  
  555. void ps_printer::set_char(int i, font *f, const environment *env, int w)
  556. {
  557.   if (i == space_char_index || invis_count > 0)
  558.     return;
  559.   unsigned char code = f->get_code(i);
  560.   style sty(f, env->size, env->height, env->slant);
  561.   if (sty.slant != 0) {
  562.     if (sty.slant > 80 || sty.slant < -80) {
  563.       error("silly slant `%1' degrees", sty.slant);
  564.       sty.slant = 0;
  565.     }
  566.   }
  567.   if (sbuf_len > 0) {
  568.     if (sbuf_len < SBUF_SIZE
  569.     && sty == sbuf_style
  570.     && sbuf_vpos == env->vpos) {
  571.       if (sbuf_end_hpos == env->hpos) {
  572.     sbuf[sbuf_len++] = code;
  573.     sbuf_end_hpos += w + sbuf_kern;
  574.     return;
  575.       }
  576.       if (sbuf_len == 1 && sbuf_kern == 0) {
  577.     sbuf_kern = env->hpos - sbuf_end_hpos;
  578.     sbuf_end_hpos = env->hpos + sbuf_kern + w;
  579.     sbuf[sbuf_len++] = code;
  580.     return;
  581.       }
  582.       /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
  583.      starting a new string. */
  584.       if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
  585.       && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
  586.     if (sbuf_space_code < 0) {
  587.       if (f->contains(space_char_index)) {
  588.         sbuf_space_code = f->get_code(space_char_index);
  589.         sbuf_space_width = env->hpos - sbuf_end_hpos;
  590.         sbuf_end_hpos = env->hpos + w + sbuf_kern;
  591.         sbuf[sbuf_len++] = sbuf_space_code;
  592.         sbuf[sbuf_len++] = code;
  593.         sbuf_space_count++;
  594.         return;
  595.       }
  596.     }
  597.     else {
  598.       int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
  599.       if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
  600.         sbuf_end_hpos = env->hpos + w + sbuf_kern;
  601.         sbuf[sbuf_len++] = sbuf_space_code;
  602.         sbuf[sbuf_len++] = code;
  603.         sbuf_space_count++;
  604.         if (diff == 1)
  605.           sbuf_space_diff_count++;
  606.         else if (diff == -1)
  607.           sbuf_space_diff_count--;
  608.         return;
  609.       }
  610.     }
  611.       }
  612.     }
  613.     flush_sbuf();
  614.   }
  615.   sbuf_len = 1;
  616.   sbuf[0] = code;
  617.   sbuf_end_hpos = env->hpos + w;
  618.   sbuf_start_hpos = env->hpos;
  619.   sbuf_vpos = env->vpos;
  620.   sbuf_style = sty;
  621.   sbuf_space_code = -1;
  622.   sbuf_space_width = 0;
  623.   sbuf_space_count = sbuf_space_diff_count = 0;
  624.   sbuf_kern = 0;
  625. }
  626.  
  627. int is_small_h(int n)
  628. {
  629.   return n < (font::res*2)/72 && n > -(font::res*10)/72;
  630. }
  631.  
  632. int is_small_v(int n)
  633. {
  634.   return n < (font::res*4)/72 && n > -(font::res*4)/72;
  635. }
  636.  
  637. static char *make_encoding_name(int encoding_index)
  638. {
  639.   static char buf[3 + INT_DIGITS + 1];
  640.   sprintf(buf, "ENC%d", encoding_index);
  641.   return buf;
  642. }
  643.  
  644. const char *const WS = " \t\n\r";
  645.  
  646. void ps_printer::define_encoding(const char *encoding, int encoding_index)
  647. {
  648.   char *vec[256];
  649.   for (int i = 0; i < 256; i++)
  650.     vec[i] = 0;
  651.   char *path;
  652.   FILE *fp = font::open_file(encoding, &path);
  653.   if (fp == 0)
  654.     fatal("can't open encoding file `%1'", encoding);
  655.   int lineno = 1;
  656.   char buf[256];
  657.   while (fgets(buf, 512, fp) != 0) {
  658.     char *p = buf;
  659.     while (isascii(*p) && isspace(*p))
  660.       p++;
  661.     if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
  662.       char *q = strtok(0, WS);
  663.       int n;
  664.       if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
  665.     fatal_with_file_and_line(path, lineno, "bad second field");
  666.       vec[n] = new char[strlen(p) + 1];
  667.       strcpy(vec[n], p);
  668.     }
  669.     lineno++;
  670.   }
  671.   a_delete path;
  672.   out.put_literal_symbol(make_encoding_name(encoding_index));
  673.   out.put_delimiter('[');
  674.   for (i = 0; i < 256; i++) {
  675.     if (vec[i] == 0)
  676.       out.put_literal_symbol(".notdef");
  677.     else {
  678.       out.put_literal_symbol(vec[i]);
  679.       a_delete vec[i];
  680.     }
  681.   }
  682.   out.put_delimiter(']').put_symbol("def");
  683. }
  684.  
  685. void ps_printer::reencode_font(ps_font *f)
  686. {
  687.   out.put_literal_symbol(f->reencoded_name)
  688.      .put_symbol(make_encoding_name(f->encoding_index))
  689.      .put_literal_symbol(f->get_internal_name())
  690.      .put_symbol("RE");
  691. }
  692.  
  693. void ps_printer::encode_fonts()
  694. {
  695.   if (next_encoding_index == 0)
  696.     return;
  697.   char *done_encoding = new char[next_encoding_index];
  698.   for (int i = 0; i < next_encoding_index; i++)
  699.     done_encoding[i] = 0;
  700.   for (font_pointer_list *f = font_list; f; f = f->next) {
  701.     int encoding_index = ((ps_font *)f->p)->encoding_index;
  702.     if (encoding_index >= 0) {
  703.       assert(encoding_index < next_encoding_index);
  704.       if (!done_encoding[encoding_index]) {
  705.     done_encoding[encoding_index] = 1;
  706.     define_encoding(((ps_font *)f->p)->encoding, encoding_index);
  707.       }
  708.       reencode_font((ps_font *)f->p);
  709.     }
  710.   }
  711.   a_delete done_encoding;
  712. }
  713.  
  714. void ps_printer::set_style(const style &sty)
  715. {
  716.   char buf[1 + INT_DIGITS + 1];
  717.   for (int i = 0; i < ndefined_styles; i++)
  718.     if (sty == defined_styles[i]) {
  719.       sprintf(buf, "F%d", i);
  720.       out.put_symbol(buf);
  721.       return;
  722.     }
  723.   if (ndefined_styles >= MAX_DEFINED_STYLES)
  724.     ndefined_styles = 0;
  725.   sprintf(buf, "F%d", ndefined_styles);
  726.   out.put_literal_symbol(buf);
  727.   const char *psname = sty.f->get_internal_name();
  728.   if (psname == 0)
  729.     fatal("no internalname specified for font `%1'", sty.f->get_name());
  730.   char *encoding = ((ps_font *)sty.f)->encoding;
  731.   if (encoding != 0) {
  732.     char *s = ((ps_font *)sty.f)->reencoded_name;
  733.     if (s == 0) {
  734.       int ei = set_encoding_index((ps_font *)sty.f);
  735.       char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
  736.       sprintf(tem, "%s@%d", psname, ei);
  737.       psname = tem;
  738.       ((ps_font *)sty.f)->reencoded_name = tem;
  739.     }
  740.     else
  741.       psname = s;
  742.   }
  743.   out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
  744.   if (sty.height != 0 || sty.slant != 0) {
  745.     int h = sty.height == 0 ? sty.point_size : sty.height;
  746.     h *= font::res/(72*font::sizescale);
  747.     int c = int(h*tan(radians(sty.slant)) + .5);
  748.     out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname)
  749.        .put_symbol("MF");
  750.   }
  751.   else {
  752.     out.put_literal_symbol(psname).put_symbol("SF");
  753.   }
  754.   defined_styles[ndefined_styles++] = sty;
  755. }
  756.  
  757. void ps_printer::set_space_code(unsigned char c)
  758. {
  759.   out.put_literal_symbol("SC").put_number(c).put_symbol("def");
  760. }
  761.  
  762. void ps_printer::end_of_line()
  763. {
  764.   flush_sbuf();
  765.   // this ensures that we do an absolute motion to the beginning of a line
  766.   output_vpos = output_hpos = -1;
  767. }
  768.  
  769. void ps_printer::flush_sbuf()
  770. {
  771.   enum {
  772.     NONE,
  773.     RELATIVE_H,
  774.     RELATIVE_V,
  775.     RELATIVE_HV,
  776.     ABSOLUTE
  777.     } motion = NONE;
  778.   int space_flag = 0;
  779.   if (sbuf_len == 0)
  780.     return;
  781.   if (output_style != sbuf_style) {
  782.     set_style(sbuf_style);
  783.     output_style = sbuf_style;
  784.   }
  785.   int extra_space = 0;
  786.   if (output_hpos < 0 || output_vpos < 0
  787.       || !is_small_h(output_hpos - sbuf_start_hpos)
  788.       || !is_small_v(output_vpos - sbuf_vpos))
  789.     motion = ABSOLUTE;
  790.   else {
  791.     if (output_hpos != sbuf_start_hpos)
  792.       motion = RELATIVE_H;
  793.     if (output_vpos != sbuf_vpos) {
  794.       if  (motion != NONE)
  795.     motion = RELATIVE_HV;
  796.       else
  797.     motion = RELATIVE_V;
  798.     }
  799.   }
  800.   if (sbuf_space_code >= 0) {
  801.     int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
  802.     if (w + sbuf_kern != sbuf_space_width) {
  803.       if (sbuf_space_code != output_space_code) {
  804.     set_space_code(sbuf_space_code);
  805.     output_space_code = sbuf_space_code;
  806.       }
  807.       space_flag = 1;
  808.       extra_space = sbuf_space_width - w - sbuf_kern;
  809.       if (sbuf_space_diff_count > sbuf_space_count/2)
  810.     extra_space++;
  811.       else if (sbuf_space_diff_count < -(sbuf_space_count/2))
  812.     extra_space--;
  813.     }
  814.   }
  815.   if (space_flag)
  816.     out.put_fix_number(extra_space);
  817.   if (sbuf_kern != 0)
  818.     out.put_fix_number(sbuf_kern);
  819.   out.put_string(sbuf, sbuf_len);
  820.   char sym[2];
  821.   sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
  822.   sym[1] = '\0';
  823.   switch (motion) {
  824.   case NONE:
  825.     break;
  826.   case ABSOLUTE:
  827.     out.put_fix_number(sbuf_start_hpos)
  828.        .put_fix_number(sbuf_vpos);
  829.     break;
  830.   case RELATIVE_H:
  831.     out.put_fix_number(sbuf_start_hpos - output_hpos);
  832.     break;
  833.   case RELATIVE_V:
  834.     out.put_fix_number(sbuf_vpos - output_vpos);
  835.     break;
  836.   case RELATIVE_HV:
  837.     out.put_fix_number(sbuf_start_hpos - output_hpos)
  838.        .put_fix_number(sbuf_vpos - output_vpos);
  839.     break;
  840.   default:
  841.     assert(0);
  842.   }
  843.   out.put_symbol(sym);
  844.   output_hpos = sbuf_end_hpos;
  845.   output_vpos = sbuf_vpos;
  846.   sbuf_len = 0;
  847. }
  848.  
  849.  
  850. void ps_printer::set_line_thickness(const environment *env)
  851. {
  852.   if (line_thickness < 0) {
  853.     if (output_draw_point_size != env->size) {
  854.       // we ought to check for overflow here
  855.       int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
  856.       out.put_fix_number(lw).put_symbol("LW");
  857.       output_draw_point_size = env->size;
  858.       output_line_thickness = -1;
  859.     }
  860.   }
  861.   else {
  862.     if (output_line_thickness != line_thickness) {
  863.       out.put_fix_number(line_thickness).put_symbol("LW");
  864.       output_line_thickness = line_thickness;
  865.       output_draw_point_size = -1;
  866.     }
  867.   }
  868. }
  869.  
  870. void ps_printer::fill_path()
  871. {
  872.   if (fill > FILL_MAX)
  873.     out.put_symbol("BL");
  874.   else
  875.     out.put_float(transform_fill(fill)).put_symbol("FL");
  876. }
  877.  
  878. void ps_printer::draw(int code, int *p, int np, const environment *env)
  879. {
  880.   if (invis_count > 0)
  881.     return;
  882.   int fill_flag = 0;
  883.   switch (code) {
  884.   case 'C':
  885.     fill_flag = 1;
  886.     // fall through
  887.   case 'c':
  888.     // troff adds an extra argument to C
  889.     if (np != 1 && !(code == 'C' && np == 2)) {
  890.       error("1 argument required for circle");
  891.       break;
  892.     }
  893.     out.put_fix_number(env->hpos + p[0]/2)
  894.        .put_fix_number(env->vpos)
  895.        .put_fix_number(p[0]/2)
  896.        .put_symbol("DC");
  897.     if (fill_flag) {
  898.       fill_path();
  899.     }
  900.     else {
  901.       set_line_thickness(env);
  902.       out.put_symbol("ST");
  903.     }
  904.     break;
  905.   case 'l':
  906.     if (np != 2) {
  907.       error("2 arguments required for line");
  908.       break;
  909.     }
  910.     set_line_thickness(env);
  911.     out.put_fix_number(p[0] + env->hpos)
  912.        .put_fix_number(p[1] + env->vpos)
  913.        .put_fix_number(env->hpos)
  914.        .put_fix_number(env->vpos)
  915.        .put_symbol("DL");
  916.     break;
  917.   case 'E':
  918.     fill_flag = 1;
  919.     // fall through
  920.   case 'e':
  921.     if (np != 2) {
  922.       error("2 arguments required for ellipse");
  923.       break;
  924.     }
  925.     out.put_fix_number(p[0])
  926.        .put_fix_number(p[1])
  927.        .put_fix_number(env->hpos + p[0]/2)
  928.        .put_fix_number(env->vpos)
  929.        .put_symbol("DE");
  930.     if (fill_flag) {
  931.       fill_path();
  932.     }
  933.     else {
  934.       set_line_thickness(env);
  935.       out.put_symbol("ST");
  936.     }
  937.     break;
  938.   case 'P':
  939.     fill_flag = 1;
  940.     // fall through
  941.   case 'p':
  942.     {
  943.       if (np & 1) {
  944.     error("even number of arguments required for polygon");
  945.     break;
  946.       }
  947.       if (np == 0) {
  948.     error("no arguments for polygon");
  949.     break;
  950.       }
  951.       out.put_fix_number(env->hpos)
  952.      .put_fix_number(env->vpos)
  953.      .put_symbol("MT");
  954.       for (int i = 0; i < np; i += 2)
  955.     out.put_fix_number(p[i])
  956.        .put_fix_number(p[i+1])
  957.        .put_symbol("RL");
  958.       out.put_symbol("CL");
  959.       if (fill_flag) {
  960.     fill_path();
  961.       }
  962.       else {
  963.     set_line_thickness(env);
  964.     out.put_symbol("ST");
  965.       }
  966.       break;
  967.     }
  968.   case '~':
  969.     {
  970.       if (np & 1) {
  971.     error("even number of arguments required for spline");
  972.     break;
  973.       }
  974.       if (np == 0) {
  975.     error("no arguments for spline");
  976.     break;
  977.       }
  978.       out.put_fix_number(env->hpos)
  979.      .put_fix_number(env->vpos)
  980.      .put_symbol("MT");
  981.       out.put_fix_number(p[0]/2)
  982.      .put_fix_number(p[1]/2)
  983.      .put_symbol("RL");
  984.       /* tnum/tden should be between 0 and 1; the closer it is to 1
  985.      the tighter the curve will be to the guiding lines; 2/3
  986.      is the standard value */
  987.       const int tnum = 2;
  988.       const int tden = 3;
  989.       for (int i = 0; i < np - 2; i += 2) {
  990.     out.put_fix_number((p[i]*tnum)/(2*tden))
  991.        .put_fix_number((p[i + 1]*tnum)/(2*tden))
  992.        .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
  993.        .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
  994.        .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
  995.        .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
  996.        .put_symbol("RC");
  997.       }
  998.       out.put_fix_number(p[np - 2] - p[np - 2]/2)
  999.      .put_fix_number(p[np - 1] - p[np - 1]/2)
  1000.      .put_symbol("RL");
  1001.       set_line_thickness(env);
  1002.       out.put_symbol("ST");
  1003.     }
  1004.     break;
  1005.   case 'a':
  1006.     {
  1007.       if (np != 4) {
  1008.     error("4 arguments required for arc");
  1009.     break;
  1010.       }
  1011.       set_line_thickness(env);
  1012.       double c[2];
  1013.       if (adjust_arc_center(p, c))
  1014.     out.put_fix_number(env->hpos + int(c[0]))
  1015.        .put_fix_number(env->vpos + int(c[1]))
  1016.        .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
  1017.        .put_float(degrees(atan2(-c[1], -c[0])))
  1018.        .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
  1019.        .put_symbol("DA");
  1020.       else
  1021.     out.put_fix_number(p[0] + p[2] + env->hpos)
  1022.        .put_fix_number(p[1] + p[3] + env->vpos)
  1023.        .put_fix_number(env->hpos)
  1024.        .put_fix_number(env->vpos)
  1025.        .put_symbol("DL");
  1026.     }
  1027.     break;
  1028.   case 't':
  1029.     {
  1030.       if (np == 0) {
  1031.     line_thickness = -1;
  1032.       }
  1033.       else {
  1034.     // troff gratuitously adds an extra 0
  1035.     if (np != 1 && np != 2) {
  1036.       error("0 or 1 argument required for thickness");
  1037.       break;
  1038.     }
  1039.     line_thickness = p[0];
  1040.       }
  1041.       break;
  1042.     }
  1043.   case 'f':
  1044.     {
  1045.       if (np != 1 && np != 2) {
  1046.     error("1 argument required for fill");
  1047.     break;
  1048.       }
  1049.       fill = p[0];
  1050.       if (fill < 0 || fill > FILL_MAX) {
  1051.     // This means fill with the current color.
  1052.     fill = FILL_MAX + 1;
  1053.       }
  1054.       break;
  1055.     }      
  1056.   default:
  1057.     error("unrecognised drawing command `%1'", char(code));
  1058.     break;
  1059.   }
  1060.  
  1061.   output_hpos = output_vpos = -1;
  1062. }
  1063.  
  1064.  
  1065. void ps_printer::begin_page(int n)
  1066. {
  1067.   out.begin_comment("Page:").comment_arg(itoa(n));
  1068.   out.comment_arg(itoa(++pages_output)).end_comment();
  1069.   output_style.f = 0;
  1070.   output_space_code = 32;
  1071.   output_draw_point_size = -1;
  1072.   output_line_thickness = -1;
  1073.   output_hpos = output_vpos = -1;
  1074.   ndefined_styles = 0;
  1075.   out.simple_comment("BeginPageSetup");
  1076.   out.put_symbol("BP");
  1077.   out.simple_comment("EndPageSetup");
  1078. }
  1079.  
  1080. void ps_printer::end_page(int)
  1081. {
  1082.   flush_sbuf();
  1083.   out.put_symbol("EP");
  1084.   if (invis_count != 0) {
  1085.     error("missing `endinvis' command");
  1086.     invis_count = 0;
  1087.   }
  1088. }
  1089.  
  1090. font *ps_printer::make_font(const char *nm)
  1091. {
  1092.   return ps_font::load_ps_font(nm);
  1093. }
  1094.  
  1095. ps_printer::~ps_printer()
  1096. {
  1097.   out.simple_comment("Trailer");
  1098.   out.put_symbol("end");
  1099.   out.simple_comment("EOF");
  1100.   if (fseek(tempfp, 0L, 0) < 0)
  1101.     fatal("fseek on temporary file failed");
  1102.   fputs("%!PS-Adobe-3.0\n", stdout);
  1103.   out.set_file(stdout);
  1104.   {
  1105.     extern const char *version_string;
  1106.     out.begin_comment("Creator:")
  1107.        .comment_arg("groff")
  1108.        .comment_arg("version")
  1109.        .comment_arg(version_string)
  1110.        .end_comment();
  1111.   }
  1112.   for (font_pointer_list *f = font_list; f; f = f->next) {
  1113.     ps_font *psf = (ps_font *)(f->p);
  1114.     rm.need_font(psf->get_internal_name());
  1115.   }
  1116.   rm.print_header_comments(out);
  1117.   out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment();
  1118.   out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment();
  1119. #if 0
  1120.   fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
  1121.       font::paperwidth*72.0/font::res,
  1122.       paper_length*72.0/font::res);
  1123. #endif
  1124.   out.begin_comment("Orientation:")
  1125.      .comment_arg(landscape_flag ? "Landscape" : "Portrait")
  1126.      .end_comment(); 
  1127.   if (ncopies != 1) {
  1128.     out.end_line();
  1129.     fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
  1130.   }
  1131.   out.simple_comment("EndComments");
  1132.   out.simple_comment("BeginProlog");
  1133.   rm.output_prolog(out);
  1134.   if (!(broken_flags & NO_SETUP_SECTION)) {
  1135.     out.simple_comment("EndProlog");
  1136.     out.simple_comment("BeginSetup");
  1137.   }
  1138.   rm.document_setup(out);
  1139.   out.put_symbol(dict_name).put_symbol("begin");
  1140.   if (ndefs > 0)
  1141.     ndefs += DEFS_DICT_SPARE;
  1142.   out.put_literal_symbol(defs_dict_name)
  1143.      .put_number(ndefs + 1)
  1144.      .put_symbol("dict")
  1145.      .put_symbol("def");
  1146.   out.put_symbol(defs_dict_name)
  1147.      .put_symbol("begin");
  1148.   out.put_literal_symbol("u")
  1149.      .put_delimiter('{')
  1150.      .put_fix_number(1)
  1151.      .put_symbol("mul")
  1152.      .put_delimiter('}')
  1153.      .put_symbol("bind")
  1154.      .put_symbol("def");
  1155.   defs += '\0';
  1156.   out.special(defs.contents());
  1157.   out.put_symbol("end");
  1158.   if (ncopies != 1)
  1159.     out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
  1160.   out.put_literal_symbol("RES").put_number(res).put_symbol("def");
  1161.   out.put_literal_symbol("PL");
  1162.   if (guess_flag)
  1163.     out.put_symbol("PLG");
  1164.   else
  1165.     out.put_fix_number(paper_length);
  1166.   out.put_symbol("def");
  1167.   out.put_literal_symbol("LS")
  1168.      .put_symbol(landscape_flag ? "true" : "false")
  1169.      .put_symbol("def");
  1170.   encode_fonts();
  1171.   out.simple_comment((broken_flags & NO_SETUP_SECTION)
  1172.              ? "EndProlog"
  1173.              : "EndSetup");
  1174.   out.end_line();
  1175.   out.copy_file(tempfp);
  1176.   fclose(tempfp);
  1177. }
  1178.  
  1179. void ps_printer::special(char *arg, const environment *env)
  1180. {
  1181.   typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
  1182.   static struct {
  1183.     const char *name;
  1184.     SPECIAL_PROCP proc;
  1185.   } proc_table[] = {
  1186.     "exec", &ps_printer::do_exec,
  1187.     "def", &ps_printer::do_def,
  1188.     "mdef", &ps_printer::do_mdef,
  1189.     "import", &ps_printer::do_import,
  1190.     "file", &ps_printer::do_file,
  1191.     "invis", &ps_printer::do_invis,
  1192.     "endinvis", &ps_printer::do_endinvis,
  1193.   };
  1194.   for (char *p = arg; *p == ' ' || *p == '\n'; p++)
  1195.     ;
  1196.   char *tag = p;
  1197.   for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
  1198.     ;
  1199.   if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
  1200.     error("X command without `ps:' tag ignored");
  1201.     return;
  1202.   }
  1203.   p++;
  1204.   for (; *p == ' ' || *p == '\n'; p++)
  1205.     ;
  1206.   char *command = p;
  1207.   for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
  1208.     ;
  1209.   if (*command == '\0') {
  1210.     error("X command without `ps:' tag ignored");
  1211.     return;
  1212.   }
  1213.   for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
  1214.     if (strncmp(command, proc_table[i].name, p - command) == 0) {
  1215.       (this->*(proc_table[i].proc))(p, env);
  1216.       return;
  1217.     }
  1218.   error("X command `%1' not recognised", command);
  1219. }
  1220.  
  1221. // A conforming PostScript document must not have lines longer
  1222. // than 255 characters (excluding line termination characters).
  1223.  
  1224. static int check_line_lengths(const char *p)
  1225. {
  1226.   for (;;) {
  1227.     const char *end = strchr(p, '\n');
  1228.     if (end == 0)
  1229.       end = strchr(p, '\0');
  1230.     if (end - p > 255)
  1231.       return 0;
  1232.     if (*end == '\0')
  1233.       break;
  1234.     p = end + 1;
  1235.   }
  1236.   return 1;
  1237. }
  1238.  
  1239. void ps_printer::do_exec(char *arg, const environment *env)
  1240. {
  1241.   flush_sbuf();
  1242.   while (csspace(*arg))
  1243.     arg++;
  1244.   if (*arg == '\0') {
  1245.     error("missing argument to X exec command");
  1246.     return;
  1247.   }
  1248.   if (!check_line_lengths(arg)) {
  1249.     error("lines in X exec command must not be more than 255 characters long");
  1250.     return;
  1251.   }
  1252.   out.put_fix_number(env->hpos)
  1253.      .put_fix_number(env->vpos)
  1254.      .put_symbol("EBEGIN")
  1255.      .special(arg)
  1256.      .put_symbol("EEND");
  1257.   output_hpos = output_vpos = -1;
  1258.   output_style.f = 0;
  1259.   output_draw_point_size = -1;
  1260.   output_line_thickness = -1;
  1261.   ndefined_styles = 0;
  1262.   if (!ndefs)
  1263.     ndefs = 1;
  1264. }
  1265.  
  1266. void ps_printer::do_file(char *arg, const environment *env)
  1267. {
  1268.   flush_sbuf();
  1269.   while (csspace(*arg))
  1270.     arg++;
  1271.   if (*arg == '\0') {
  1272.     error("missing argument to X file command");
  1273.     return;
  1274.   }
  1275.   const char *filename = arg;
  1276.   do {
  1277.     ++arg;
  1278.   } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
  1279.   out.put_fix_number(env->hpos)
  1280.      .put_fix_number(env->vpos)
  1281.      .put_symbol("EBEGIN");
  1282.   rm.import_file(filename, out);
  1283.   out.put_symbol("EEND");
  1284.   output_hpos = output_vpos = -1;
  1285.   output_style.f = 0;
  1286.   output_draw_point_size = -1;
  1287.   output_line_thickness = -1;
  1288.   ndefined_styles = 0;
  1289.   if (!ndefs)
  1290.     ndefs = 1;
  1291. }
  1292.  
  1293. void ps_printer::do_def(char *arg, const environment *)
  1294. {
  1295.   flush_sbuf();
  1296.   while (csspace(*arg))
  1297.     arg++;
  1298.   if (!check_line_lengths(arg)) {
  1299.     error("lines in X def command must not be more than 255 characters long");
  1300.     return;
  1301.   }
  1302.   defs += arg;
  1303.   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
  1304.     defs += '\n';
  1305.   ndefs++;
  1306. }
  1307.  
  1308. // Like def, but the first argument says how many definitions it contains.
  1309.  
  1310. void ps_printer::do_mdef(char *arg, const environment *)
  1311. {
  1312.   flush_sbuf();
  1313.   char *p;
  1314.   int n = (int)strtol(arg, &p, 10);
  1315.   if (n == 0 && p == arg) {
  1316.     error("first argument to X mdef must be an integer");
  1317.     return;
  1318.   }
  1319.   if (n < 0) {
  1320.     error("out of range argument `%1' to X mdef command", int(n));
  1321.     return;
  1322.   }
  1323.   arg = p;
  1324.   while (csspace(*arg))
  1325.     arg++;
  1326.   if (!check_line_lengths(arg)) {
  1327.     error("lines in X mdef command must not be more than 255 characters long");
  1328.     return;
  1329.   }
  1330.   defs += arg;
  1331.   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
  1332.     defs += '\n';
  1333.   ndefs += n;
  1334. }
  1335.  
  1336. void ps_printer::do_import(char *arg, const environment *env)
  1337. {
  1338.   flush_sbuf();
  1339.   while (*arg == ' ' || *arg == '\n')
  1340.     arg++;
  1341.   for (char *p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
  1342.     ;
  1343.   if (*p != '\0')
  1344.     *p++ = '\0';
  1345.   int parms[6];
  1346.   int nparms = 0;
  1347.   while (nparms < 6) {
  1348.     char *end;
  1349.     long n = strtol(p, &end, 10);
  1350.     if (n == 0 && end == p)
  1351.       break;
  1352.     parms[nparms++] = int(n);
  1353.     p = end;
  1354.   }
  1355.   if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
  1356.     error("scaling indicators not allowed in arguments for X import command");
  1357.     return;
  1358.   }
  1359.   while (*p == ' ' || *p == '\n')
  1360.     p++;
  1361.   if (nparms < 5) {
  1362.     if (*p == '\0')
  1363.       error("too few arguments for X import command");
  1364.     else
  1365.       error("invalid argument `%1' for X import command", p);
  1366.     return;
  1367.   }
  1368.   if (*p != '\0') {
  1369.     error("superflous argument `%1' for X import command", p);
  1370.     return;
  1371.   }
  1372.   int llx = parms[0];
  1373.   int lly = parms[1];
  1374.   int urx = parms[2];
  1375.   int ury = parms[3];
  1376.   int desired_width = parms[4];
  1377.   int desired_height = parms[5];
  1378.   if (desired_width <= 0) {
  1379.     error("bad width argument `%1' for X import command: must be > 0",
  1380.       desired_width);
  1381.     return;
  1382.   }
  1383.   if (nparms == 6 && desired_height <= 0) {
  1384.     error("bad height argument `%1' for X import command: must be > 0",
  1385.       desired_height);
  1386.     return;
  1387.   }
  1388.   if (llx == urx) {
  1389.     error("llx and urx arguments for X import command must not be equal");
  1390.     return;
  1391.   }
  1392.   if (lly == ury) {
  1393.     error("lly and ury arguments for X import command must not be equal");
  1394.     return;
  1395.   }
  1396.   if (nparms == 5) {
  1397.     int old_wid = urx - llx;
  1398.     int old_ht = ury - lly;
  1399.     if (old_wid < 0)
  1400.       old_wid = -old_wid;
  1401.     if (old_ht < 0)
  1402.       old_ht = -old_ht;
  1403.     desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
  1404.   }
  1405.   if (env->vpos - desired_height < 0)
  1406.     warning("top of imported graphic is above the top of the page");
  1407.   out.put_number(llx)
  1408.      .put_number(lly)
  1409.      .put_fix_number(desired_width)
  1410.      .put_number(urx - llx)
  1411.      .put_fix_number(-desired_height)
  1412.      .put_number(ury - lly)
  1413.      .put_fix_number(env->hpos)
  1414.      .put_fix_number(env->vpos)
  1415.      .put_symbol("PBEGIN");
  1416.   rm.import_file(arg, out);
  1417.   // do this here just in case application defines PEND
  1418.   out.put_symbol("end");
  1419.   out.put_symbol("PEND");
  1420. }
  1421.  
  1422. void ps_printer::do_invis(char *, const environment *)
  1423. {
  1424.   invis_count++;
  1425. }
  1426.  
  1427. void ps_printer::do_endinvis(char *, const environment *)
  1428. {
  1429.   if (invis_count == 0)
  1430.     error("unbalanced `endinvis' command");
  1431.   else
  1432.     --invis_count;
  1433. }
  1434.  
  1435. printer *make_printer()
  1436. {
  1437.   return new ps_printer;
  1438. }
  1439.  
  1440. static void usage();
  1441.  
  1442. int main(int argc, char **argv)
  1443. {
  1444.   program_name = argv[0];
  1445.   static char stderr_buf[BUFSIZ];
  1446.   setbuf(stderr, stderr_buf);
  1447.   int c;
  1448.   while ((c = getopt(argc, argv, "F:glc:w:vb:")) != EOF)
  1449.     switch(c) {
  1450.     case 'v':
  1451.       {
  1452.     extern const char *version_string;
  1453.     fprintf(stderr, "grops version %s\n", version_string);
  1454.     fflush(stderr);
  1455.     break;
  1456.       }
  1457.     case 'c':
  1458.       if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
  1459.     error("bad number of copies `%s'", optarg);
  1460.     ncopies = 1;
  1461.       }
  1462.       break;
  1463.     case 'g':
  1464.       guess_flag = 1;
  1465.       break;
  1466.     case 'l':
  1467.       landscape_flag = 1;
  1468.       break;
  1469.     case 'F':
  1470.       font::command_line_font_dir(optarg);
  1471.       break;
  1472.     case 'w':
  1473.       if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
  1474.     error("bad linewidth `%s'", optarg);
  1475.     linewidth = -1;
  1476.       }
  1477.       break;
  1478.     case 'b':
  1479.       // XXX check this
  1480.       broken_flags = atoi(optarg);
  1481.       bflag = 1;
  1482.       break;
  1483.     case '?':
  1484.       usage();
  1485.       break;
  1486.     default:
  1487.       assert(0);
  1488.     }
  1489.   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
  1490.   if (optind >= argc)
  1491.     do_file("-");
  1492.   else {
  1493.     for (int i = optind; i < argc; i++)
  1494.       do_file(argv[i]);
  1495.   }
  1496.   delete pr;
  1497.   exit(0);
  1498. }
  1499.  
  1500. static void usage()
  1501. {
  1502.   fprintf(stderr, "usage: %s [-glv] [-b n] [-c n] [-w n] [-F dir] [files ...]\n",
  1503.       program_name);
  1504.   exit(1);
  1505. }
  1506.